package com.android.server.mock;

import android.annotation.Nullable;
import android.content.Context;
import android.os.mock.MockManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;

import com.android.internal.telephony.TelephonyProperties;
import android.hardware.Sensor;
import com.android.server.mock.accessibility.MockAccessibilityImpl;
import com.android.server.mock.memory.MockMemoryImpl;
import com.android.server.mock.storage.MockStatFsImpl;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import android.os.SystemProperties;//lucas

public class DataParser {
    public static final String TAG = "DataParser";

    // 需要存属性的全存这个文件，标准prop文件格式，ro.cust.ps=1，一行一个
    private static final String mCustPropFileName = "cust.prop";
    private static final String mStaticEnvFileName = "static_env.json";
    private static final String mDynamicEnvFileName = "dynamic_env.json";
    private static final String mUAFileName = "cust_ua.txt";

    //The mock bluetooth info (name#mac_address)
    private String bluetoothName = "";
    private String bluetoothMac = "";
    //The mock networkinterface list info.
    private String networkInterfaceList = "";
    private String mBatteryInfo = "";
    private String mDrmInfo = "";
    private String mSimInfo = "";
    private String mBaseStationInfo = "";
    private String mNeighboringCellInfo = "";
    private String mWifisInfo = "";
    private String mUserAgent = "";
    private Map<String, Object> resultMapPsUsed = new HashMap<>();
    private String mSensorInfo = "";
    private String mDisplays = "";
    private String mInputMethodInfo = "";
    private String mInputDeviceInfo = "";
    // add by yhy (start)
    private String mSettingsInfo = "";
    // add by yhy (end)

    public String workspaceDir;
    public String userWorkspaceDir;
    public Context mContext;

    // 改机是否需要重启后生效，ro属性还无法重新赋值，需要重启来生效
    private boolean rebootRequired = false;

    public DataParser(Context context, String dir) {
        mContext = context;
        workspaceDir = dir;
    }

    public ParserResult handleStaticEnv(String data, int userId) {
        File dir = prepareWorkspaceDir(userId);
        if (TextUtils.isEmpty(data)) {
            return new ParserResult(Status.FAILED, "not found static env!");
        }
        String content;
        if (data.startsWith("/")) {
            File file = new File(data);
            content = FileUtil.getFileContent(file, false);
            // TODO: 对content解密
            if (TextUtils.isEmpty(content)) {
                return new ParserResult(Status.FAILED, "static env file is empty!");
            }
        } else {
            content = data;
        }

        try {
            // check json format
            new JSONObject(content);
        } catch (JSONException e) {
            e.printStackTrace();
            return new ParserResult(Status.FAILED, "static env json format error = " + e.getMessage());
        }

        File staticEnv = new File(dir, mStaticEnvFileName);
        // TODO: 对content加密
        if (!FileUtil.saveToFile(staticEnv.getAbsolutePath(), content)) {
            Slog.e(TAG, "save static env failed");
            return new ParserResult(Status.FAILED, "save static env failed");
        }

        initCustPropFile(dir);

        if (rebootRequired) {
            return new ParserResult(Status.SUCCESS, "static env saved, please reboot");
        }

        loadCustProp(userId);

        return loadStaticEnv(userId);
    }

    // 解析数据, 并实时生效
    public ParserResult loadStaticEnv(int userId) {
        File dir = prepareWorkspaceDir(userId);
        File staticEnv = new File(dir, mStaticEnvFileName);
        if (!staticEnv.exists() || staticEnv.length() <= 0) {
            return new ParserResult(Status.FAILED, "static env not found");
        }

        return parseStaticEnv(staticEnv, dir);
    }

    public ParserResult parseDynamicEnv(String data) {
        // TODO:
        try {
            JSONObject obj = new JSONObject(data);
            parseBaseStationInfo(obj, null);
            parseWifisInfo(obj, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ParserResult(Status.SUCCESS, null);
    }

    // 获取模拟数据
    public String getInfo(String key) {
        switch (key) {
            case MockManager.KEY_BATTERY:
                Slog.d(TAG, "battery=" + mBatteryInfo);
                return mBatteryInfo;
            case MockManager.KEY_DRM:
                Slog.d(TAG, "drm=" + mDrmInfo);
                return mDrmInfo;
            case MockManager.KEY_SIMCARDS:
                Slog.d(TAG, "simCards=" + mSimInfo);
                return mSimInfo;
            case MockManager.KEY_BASE_STATION:
                Slog.d(TAG, "baseStation=" + mBaseStationInfo);
                return mBaseStationInfo;
            case MockManager.KEY_NEIGHBORING_CELL_INFO:
                Slog.d(TAG, "mNeighboringCell=" + mNeighboringCellInfo);
                return mNeighboringCellInfo;
            case MockManager.KEY_WIFIS_INFO:
                Slog.d(TAG, "mWifisInfo=" + mWifisInfo);
                return mWifisInfo;
            case MockManager.KEY_ANDROID_ID:
                return SystemProperties.get("ro.cust.androidid", "");
            case MockManager.KEY_FINGERPRINT:
                Slog.d(TAG, "fingerprint");
                /*{"hwName":"samsung/o1quew/o1q:12/SP1A.210812.016/G991U1UES5CVCB:user/release-keys","detected":true,"enrolled":true}*/
                return SystemProperties.get("ro.cust.fingerprint.name", "");//lucas
            case MockManager.KEY_NFC:
                Slog.d(TAG, "nfc");
                return SystemProperties.get("ro.cust.nfc", "");
            case MockManager.KEY_SENSORS:
                Slog.d(TAG, "sensors");
                return mSensorInfo;
            case MockManager.KEY_INPUT_METHOD:
                return mInputMethodInfo;
            case MockManager.KEY_INPUT_DEVICE:
                return mInputDeviceInfo;
            case MockManager.KEY_BLUETOOTH_NAME:
                return bluetoothName;
            case MockManager.KEY_BLUETOOTH_MAC:
                return bluetoothMac;
            case MockManager.KEY_NET_INTERFACE:
                return networkInterfaceList;
            case MockManager.KEY_USER_AGENT:
                return mUserAgent;
            case MockManager.KEY_WIFI_MAC:
                return SystemProperties.get("ro.cust.wifi.mac", "");
            case MockManager.KEY_DISPLAY:
                return mDisplays;
            // add by yhy (start)
            case MockManager.KEY_SETTINGS:
                return mSettingsInfo;
            // add by yhy (end)
            // TODO: cases
            default:
                Slog.e(TAG, "Unknown key " + key);
        }
        return null;
    }

    private ParserResult parseStaticEnv(File file, File dir) {
        JSONObject objAll;
        try {
            String content = FileUtil.getFileContent(file, false);
            // TODO: 对content解密
            if (TextUtils.isEmpty(content)) {
                return new ParserResult(Status.FAILED, "static env is empty!");
            }
            objAll = new JSONObject(content);
        } catch (JSONException e) {
            e.printStackTrace();
            return new ParserResult(Status.FAILED, "static env parse error = " + e.getMessage());
        }
        ParserResult result;

        JSONObject deviceInfo;
        try {
            deviceInfo = objAll.getJSONObject("deviceInfo");
        } catch (JSONException e) {
            e.printStackTrace();
            return new ParserResult(Status.FAILED, "device info parse failed, " + e.getMessage());
        }
        result = parseBuildAndVersionInfo(deviceInfo, dir);
        // build信息必须有
        if (result.status != Status.SUCCESS) {
            return result;
        }

        JSONObject userDeviceInfo;
        try {
            userDeviceInfo = objAll.getJSONObject("userDeviceInfo");
        } catch (JSONException e) {
            return new ParserResult(Status.FAILED, "user device info parse failed");
        }

        result = parseAndroidIdInfo(userDeviceInfo, dir);

        if (result.status != Status.SUCCESS) {
            return result;
        }

        parsePropsInfo(deviceInfo, dir);
        parseBatteryInfo(deviceInfo, dir);
        parseCameraInfo(deviceInfo, dir);
        parseCpuInfo(deviceInfo, dir);

        parseDiskInfo(deviceInfo, dir);
        parseStatfsInfo(deviceInfo, dir);
        parseVolumeInfo(deviceInfo, dir);

        parseDisplayInfo(deviceInfo, dir);
        parseFingerprintInfo(deviceInfo, dir);
        parseInputDeviceInfo(deviceInfo, dir);
        // add by yhy (start)
        parseSettingsInfo(deviceInfo, dir);
        // add by yhy (end)

        parseKernelInfo(deviceInfo, dir);
        parseMemoryInfo(deviceInfo, dir);
        parseMmcInfo(deviceInfo, dir);
        parseMountsInfo(deviceInfo, dir);
        parseSelfMountsInfo(deviceInfo, dir);
        parseNetworkInterfaceInfo(deviceInfo, dir);
        parseNfcInfo(deviceInfo, dir);
        parseScreenInfo(deviceInfo, dir);
        parseSensorInfo(deviceInfo, dir);
        parseUsbInfo(deviceInfo, dir);

        parseAvailableFeaturesListInfo(userDeviceInfo, dir);
        parseBluetoothInfo(userDeviceInfo, dir);
        parseDeviceSignatureMapInfo(userDeviceInfo, dir);
        parseDrmInfo(userDeviceInfo, dir);
        parseInputMethodInfo(userDeviceInfo, dir);
        parseInstalledAccessibilityServiceListInfo(userDeviceInfo, dir);
        parseSimCardsInfo(userDeviceInfo, dir);
        parseSystemSharedLibraryNamesListInfo(userDeviceInfo, dir);
        parseTimezoneInfo(userDeviceInfo, dir);
        parseUserAgent(userDeviceInfo, dir);
        parseWifiMacInfo(userDeviceInfo, dir);
        // TODO: 解析其它数据
        return new ParserResult(Status.SUCCESS, null);
    }

    private ParserResult parseBuildAndVersionInfo(JSONObject deviceInfo, File dir) {
        JSONObject buildObject;
        JSONObject versionObject;
        String message = "";


        if (deviceInfo.has("build")) {
            try {
                JSONObject jsonBuild = deviceInfo.getJSONObject("build");

                Slog.d(TAG, "lucas : loadEnv  parseBuildAndVersionInfo entry jsonBuild");

                if ((jsonBuild != null) && (jsonBuild.length() > 0)) {
                    //SystemProperties.set("s.ro.product.brand", "samsung");
                    String product = jsonBuild.optString("product", "");
                    Slog.d(TAG, "lucas : jsonBuild product = " + product);
                    SystemProperties.set("ro.cust.product.product.name", product);

                    String abilist = jsonBuild.optString("abilist", "");
                    Slog.d(TAG, "lucas : jsonBuild abilist = " + abilist);
                    SystemProperties.set("ro.cust.vendor.product.cpu.abilist", abilist + "");

                    String baseband = jsonBuild.optString("baseband", "");
                    SystemProperties.set("ro.cust.boot.baseband", baseband);

                    String board = jsonBuild.optString("board", "");
                    SystemProperties.set("ro.cust.product.board", board);

                    String bootloader = jsonBuild.optString("bootloader", "");
                    SystemProperties.set("ro.cust.bootloader", bootloader);

                    String brand = jsonBuild.optString("brand", "");
                    SystemProperties.set("ro.cust.product.product.brand", brand);

                    String characteristic = jsonBuild.optString("characteristic", "");
                    SystemProperties.set("ro.cust.build.characteristics", characteristic);

                    String container = jsonBuild.optString("container", "");
                    SystemProperties.set("ro.cust.container.netlimit", container);

                    String device = jsonBuild.optString("device", "");
                    SystemProperties.set("ro.cust.product.product.device", device);

                    String display = jsonBuild.optString("display", "");
                    SystemProperties.set("ro.cust.build.display.id", display);

                    String fingerprint = jsonBuild.optString("fingerprint", "");
                    SystemProperties.set("ro.cust.bootimage.build.fingerprint",fingerprint);

                    String hardware = jsonBuild.optString("hardware", "");
                    SystemProperties.set("ro.cust.boot.hardware", hardware);

                    String host = jsonBuild.optString("host", "");
                    SystemProperties.set("ro.cust.build.host", host);

                    String id = jsonBuild.optString("id", "");
                    SystemProperties.set("ro.cust.product.build.id", id);

                    String manufacturer = jsonBuild.optString("manufacturer", "");
                    SystemProperties.set("ro.cust.product.product.manufacturer", manufacturer);

                    String model = jsonBuild.optString("model", "");
                    SystemProperties.set("ro.cust.product.product.model", model);

                    String radio = jsonBuild.optString("radio", "");
                    SystemProperties.set("ro.cust.gsm.version.baseband", radio);

                    String tags = jsonBuild.optString("tags", "");
                    SystemProperties.set("ro.cust.product.build.tags", tags);

                    String time = jsonBuild.optString("time", "");
                    SystemProperties.set("ro.cust.product.build.date.utc", time);

                    Boolean trebleEnabled = jsonBuild.optBoolean("trebleEnabled", true);
                    SystemProperties.set("ro.cust.treble.enabled", trebleEnabled.toString());

                    String type = jsonBuild.optString("type", "");
                    SystemProperties.set("ro.cust.product.build.type", type);

                    String user = jsonBuild.optString("user", "");
                    SystemProperties.set("ro.cust.build.user", user);

                    SystemProperties.set("ro.cust.boot.serialno", "unknown");
                    Slog.w(TAG, "lucas : buildJson = " + jsonBuild.toString());
                    message = "success";
                }
            } catch (JSONException e) {
                message = e.getMessage();
            }
        } else {
            message = "No build mock data.";
            return new ParserResult(Status.FAILED, "build not found");
        }

        Slog.d(TAG, String.format("build parse result: %s", message));

        if (deviceInfo.has("version")) {
            try {
                versionObject = deviceInfo.getJSONObject("version");
                if ((versionObject != null) && (versionObject.length() > 0)) {
                    String baseOs = versionObject.optString("baseOs", "");
                    SystemProperties.set("ro.cust.build.version.base_os", baseOs);

                    String codename = versionObject.optString("codename", "");
                    SystemProperties.set("ro.cust.build.version.all_codenames", codename);

                    String incremental = versionObject.optString("incremental", "");
                    SystemProperties.set("ro.cust.product.build.version.incremental", incremental);

                    String release = versionObject.optString("release", "");
                    SystemProperties.set("ro.cust.product.build.version.release", release);

                    String sdk = versionObject.optString("sdk", "");
                    SystemProperties.set("ro.cust.product.build.version.sdk", sdk);

                    String securityPatch = versionObject.optString("securityPatch", "");
                    SystemProperties.set("ro.cust.build.version.security_patch", securityPatch);

                    Slog.w(TAG, "lucas : versionObject = " + versionObject.toString());
                }
            } catch (JSONException e) {
                e.printStackTrace();
                Slog.d(TAG, "lucas : loadEnv  parseBuildAndVersionInfo entry versionObject = error");
            }
        } else {
            return new ParserResult(Status.FAILED, "version not found");
        }

        // TODO:
        return new ParserResult(Status.SUCCESS, null);
    }

    private void parsePropsInfo(JSONObject deviceInfo, File dir) {
        if (deviceInfo.has("props")) {
            String strProps = deviceInfo.optString("props");
            if (!TextUtils.isEmpty(strProps)) {
                Map<String, Object> resultMapProps = parseData(strProps);
                resultMapProps.forEach((key, value) -> {
                    switch (key) {
                        case "ro.adb.secure":
                        case "ro.boot.selinux":
                        case "ro.boot.vbmeta.device_state":
                        case "ro.boot.verifiedbootstate":
                        case "ro.boot.veritymode":
                        case "ro.build.description":
                        case "ro.build.flavor":
                        case "ro.debuggable":
                        case "ro.secure":

                        case "ro.bootimage.build.date.utc":
                        case "ro.odm.build.date.utc":
                        case "ro.vendor.build.date.utc":
                        case "ro.bootimage.build.date":
                        case "ro.odm.build.date":
                        case "ro.vendor.build.date":
                        case "ro.build.date":
                        case "ro.product.build.date":
                        case "ro.system.build.date":
                            SystemProperties.set(key.replace("ro.", "ro.cust.prop."), value.toString());
                            Slog.w(TAG, "PropsInfo : key = " + key + " ,value = " + value.toString());
                            break;
                        //Mi 10 case
                        case "ro.miui.ui.version.name":
                        case "ro.miui.notch":
                        case "ro.miui.cust_hardware":
                        case "ro.miui.has_handy_mode_sf":
                        case "ro.miui.ui.version.code":
                        case "ro.miui.has_gmscore":
                        case "ro.miui.has_cust_partition":
                        case "ro.miui.restrict_imei":
                        case "ro.miui.ui.font.theme_apply":
                        case "ro.miui.mcc":
                        case "ro.mi.development":
                        case "ro.miui.pm.install.buffer.size":
                        case "ro.miui.cust_variant":
                        case "ro.miui.has_real_blur":
                        case "ro.miui.support_miui_ime_bottom":
                        case "ro.miui.build.region":
                        case "ro.miui.ui.fonttype":
                        case "ro.miui.remove_uri_80_flag":
                        case "ro.miui.mnc":
                        case "ro.miui.has_security_keyboard":
                        //reverse case
                        case "ro.build.version.emui":
                        case "ro.config.hw_codec_support":
                        case "persist.sys.aweme.hdsupport":
                        case "ro.product.tiktok.partnerid":
                        case "ro.aliyun.clouduuid":
                        case "ro.sys.aliyun.clouduuid":
                        case "persist.pxr.product.forcename":
                        case "pxr.vendorhw.product.name":
                        case "debug.layout":
                        case "ro.oplus.display.reset_two_pq_layer":
                        case "ro.smartisan.version":
                        case "ro.vivo.os.name":
                        case "ro.vivo.os.build.display.id":
                        case "ro.appsflyer.preinstall.path":
                        case "ro.build.version.magic":
                        case "ro.build.version.opporom":
                        case "ro.vivo.os.version":
                        case "ro.rom.version":
                        case "ro.build.nubia.rom.name":
                        case "ro.build.nubia.rom.code":
                        case "ro.com.google.gmsversion":
                        case "ro.hardware.egl":
                        case "ro.cdma.home.operator.numeric":
                        case "ro.mediatek.platform":
                        case "ro.kernel.qemu":
                        case "ro.huawei.build.display.id":
                        case "ro.build.version.oplusrom":
                        case "ro.vivo.product.version":
                        case "ro.build.uiversion":
                        case "ro.letv.release.version":
                        case "ro.gn.sv.version":
                        case "hw_sc.build.platform.version":
                        case "hw_sc.build.os.apiversion":
                        case "hw_sc.build.os.releasetype":
                        case "sys.super_power_save":
                        case "qemu.hw.mainkeys":
                            SystemProperties.set(key, value.toString());
                            resultMapPsUsed.put(key, value.toString());
                            break;
                        default:
                            // 当value不匹配任何case值时执行的代码
                            break;
                    }
                    });
                }
        } else {
            Log.d(TAG, "No props mock data!");
        }
    }
    private void parseBatteryInfo(JSONObject deviceInfo, File dir) {
        try {
            JSONObject obj = deviceInfo.optJSONObject("battery");
            mBatteryInfo = obj == null ? "" : obj.toString();
        } catch (Exception e) {
            Log.d(TAG, "parseBatteryInfo exception=" + e);
        }
    }

    private void parseCameraInfo(JSONObject deviceInfo, File dir) {
        try {
            JSONArray array = deviceInfo.optJSONArray("cameras");
            if (array == null) {
                Log.d(TAG, "parseCameraInfo no cameras");
                return;
            }
            int len = array.length();
            len = Math.min(len, 2);
            if (len > 0) {
                String info = "";//0,90,1##1,270,1
                StringBuffer sb = new StringBuffer();
                for (int i = 0; i < len; i++) {
                    JSONObject obj = array.optJSONObject(i);
                    if (!info.isEmpty()) {
                        info += "##";
                    }
                    info += obj.optInt("facing") + "," + obj.optInt("orientation") + "," + (obj.optBoolean("canDisableShutterSound") ? "1" : "0");
                    if (sb.length() > 0) {
                        sb.append("##");
                    }
                    sb.append(obj.optString("resolution"));
                    if (false) {
                        JSONObject tmp = obj.optJSONObject("previewSize");
                        int width, height;
                        if (tmp != null) {
                            width = tmp.optInt("width");
                            height = tmp.optInt("height");
                            if (width > 0 && height > 0) {
                                sb.append("preview-size=" + width + "x" + height + ";");
                                sb.append("video-size=" + width + "x" + height + ";");
                                sb.append("preferred-preview-size-for-video=" + width + "x" + height + ";");
                            }
                        }
                        JSONArray sizes = obj.optJSONArray("previewSizeValues");
                        if (sizes != null && sizes.length() > 0) {
                            for (int j = 0; j < sizes.length(); j++) {
                                JSONObject param = sizes.optJSONObject(j);
                                width = param.optInt("width");
                                height = param.optInt("height");
                                if (j == 0) {
                                    sb.append("preview-size-values=" + width + "x" + height);
                                } else {
                                    sb.append("," + width + "x" + height);
                                }
                            }
                            sb.append(";");
                        }
                        sizes = obj.optJSONArray("videoSizeValues");
                        if (sizes != null && sizes.length() > 0) {
                            for (int j = 0; j < sizes.length(); j++) {
                                JSONObject param = sizes.optJSONObject(j);
                                width = param.optInt("width");
                                height = param.optInt("height");
                                if (j == 0) {
                                    sb.append("video-size-values=" + width + "x" + height);
                                } else {
                                    sb.append("," + width + "x" + height);
                                }
                            }
                            sb.append(";");
                        }
                        String str = obj.optString("previewFormat");
                        if (!str.isEmpty()) {
                            sb.append("preview-format=" + str + ";");
                        }
                        str = obj.optString("previewFormatValues");
                        if (!str.isEmpty()) {
                            sb.append("preview-format-values=" + str + ";");
                        }
                        tmp = obj.optJSONObject("pictureSize");
                        if (tmp != null) {
                            width = tmp.optInt("width");
                            height = tmp.optInt("height");
                            if (width > 0 && height > 0) {
                                sb.append("picture-size=" + width + "x" + height + ";");
                            }
                        }
                        sizes = obj.optJSONArray("pictureSizeValues");
                        if (sizes != null && sizes.length() > 0) {
                            for (int j = 0; j < sizes.length(); j++) {
                                JSONObject param = sizes.optJSONObject(j);
                                width = param.optInt("width");
                                height = param.optInt("height");
                                if (j == 0) {
                                    sb.append("picture-size-values=" + width + "x" + height);
                                } else {
                                    sb.append("," + width + "x" + height);
                                }
                            }
                            sb.append(";picture-format=jpeg;picture-format-values=jpeg;");
                        }
                    }
                }
                nativeSaveFile("/data/misc/camera_param.txt", sb.toString());
                SystemProperties.set("ro.cust.camera.info", info);
                Log.d(TAG, "parseCameraInfo ok");
            }
        } catch (Exception e) {
            Log.d(TAG, "parseCameraInfo exception=" + e + ", path=" + dir.getAbsolutePath());
            SystemProperties.set("ro.cust.camera.info", "0");
        }
    }

    private void parseCpuInfo(JSONObject deviceInfo, File dir) {
        // TODO:
    }

    private void parseDiskInfo(JSONObject deviceInfo, File dir) {
        // see parse statfs
    }

    private void parseStatfsInfo(JSONObject deviceInfo, File dir) {
        boolean result = MockStatFsImpl.getInstance().parseStatfsInfo(deviceInfo);
        Slog.d(TAG, "parseStatfsInfo result " + result);
    }

    private void parseVolumeInfo(JSONObject deviceInfo, File dir) {
        // see parse statfs
    }

    private void parseDisplayInfo(JSONObject deviceInfo, File dir) {
        String message = "";

        if(deviceInfo.has("displays")) {
            try {
                JSONArray displayArray = deviceInfo.getJSONArray("displays");
                if (displayArray != null && displayArray.length() > 0) {
                    mDisplays = displayArray.toString();
                    message = "success";
                } else {
                    message = "parseDisplayInfo: no network interface info";
                }
            } catch (JSONException e) {
                message = e.getMessage();
            }
        }
        Slog.d(TAG,"parseDisplayInfo " + message);
    }

    private void parseFingerprintInfo(JSONObject deviceInfo, File dir) {
        Status status = Status.FAILED;
        String message = "";
        JSONObject jsonFingerprint;

        if (deviceInfo.has("fingerprint")) {
            try {
                jsonFingerprint = deviceInfo.getJSONObject("fingerprint");

                if ((jsonFingerprint != null) && (jsonFingerprint.length() > 0)) {
                    SystemProperties.set("ro.cust.fingerprint.name", jsonFingerprint.toString());
                    status = Status.SUCCESS;
                    message = "success";
                }
            } catch (JSONException e) {
                message = e.getMessage();
            }
        }else {
            message = "No fingerprint mock data.";
        }

        Slog.d(TAG,String.format("fingerprint parse result: %s",message));
    }

    private void parseInputDeviceInfo(JSONObject deviceInfo, File dir) {
        if (!deviceInfo.has("inputDevice")) {
            return;
        }
        try {
            mInputDeviceInfo = deviceInfo.getJSONArray("inputDevice").toString();
        } catch (JSONException e) {
            Log.e(TAG, "parseInputDeviceInfo: e:" + e);
        }
    }

    private void parseKernelInfo(JSONObject deviceInfo, File dir) {
        // TODO:
    }

    private void parseMemoryInfo(JSONObject deviceInfo, File dir) {
        boolean ret = MockMemoryImpl.getInstance().parseMemoryInfo(deviceInfo);
        Log.d(TAG, "parseMemoryInfo: " + ret);
    }

    private void parseMmcInfo(JSONObject deviceInfo, File dir) {
        // TODO:
    }

    private void parseMountsInfo(JSONObject deviceInfo, File dir) {
        // TODO:
    }

    private void parseSelfMountsInfo(JSONObject deviceInfo, File dir) {
        // TODO:
    }

    private void parseNetworkInterfaceInfo(JSONObject deviceInfo, File dir) {
        if(deviceInfo.has("networkInterface")) {
            try {
                JSONArray networkInterfaceArray = deviceInfo.getJSONArray("networkInterface");
                if (networkInterfaceArray != null && networkInterfaceArray.length() > 0) {
                    networkInterfaceList = networkInterfaceArray.toString();
                } else {
                    Log.d(TAG, "parseNetworkInterfaceInfo: no network interface info");
                }
            } catch (JSONException e) {
                Log.e(TAG, "parseNetworkInterfaceInfo: ", e);
            }
        }
    }

    private void parseNfcInfo(JSONObject deviceInfo, File dir) {
        String message = "";

        if (deviceInfo.has("nfc")) {
            try {
                JSONObject jsonFingerprint = deviceInfo.getJSONObject("nfc");

                if((jsonFingerprint != null) && (jsonFingerprint.length() > 0)){
                    SystemProperties.set("ro.cust.nfc", jsonFingerprint.toString());
                    message = "success";
                }
            } catch (JSONException e) {
                message = e.getMessage();
            }
        }else {
            message = "No nfc mock data.";
        }

        Slog.d(TAG,String.format("nfc parse result: %s",message));
    }

    private void parseScreenInfo(JSONObject deviceInfo, File dir) {
        //boolean result = MockDisplayImpl.getInstance().parseScreenInfo(deviceInfo);
        //Slog.d(TAG,String.format("Parse screen result: %b", result));
    }

    private void parseSensorInfo(JSONObject deviceInfo, File dir) {
        try {
            JSONArray sensorList = deviceInfo.optJSONArray("sensors");
            if (sensorList != null || sensorList.length() > 0) {
                mSensorInfo = sensorList.toString();
            }
        } catch (Exception e) {
            Log.d(TAG, "parseSensorInfo exception=" + e);
        }
    }

    private void parseUsbInfo(JSONObject deviceInfo, File dir) {
        // TODO:
    }

    private ParserResult parseAndroidIdInfo(JSONObject userDeviceInfo, File dir) {
        String message = "";

        String androidId = userDeviceInfo.optString("androidId");

        if (!TextUtils.isEmpty(androidId)) {
            SystemProperties.set("ro.cust.androidid", androidId);
            message = "success";
            return new ParserResult(Status.SUCCESS, "");
        } else {
            message = "No androidid mock data.";
        }
        return new ParserResult(Status.FAILED, message);
    }

    private void parseAvailableFeaturesListInfo(JSONObject userDeviceInfo, File dir) {
        // TODO:
    }

    private void parseBluetoothInfo(JSONObject userDeviceInfo, File dir) {
        if(userDeviceInfo.has("bluetooth")) {
            try {
                JSONObject btInfo = userDeviceInfo.getJSONObject("bluetooth");
                if (btInfo != null) {
                    bluetoothName = btInfo.getString("name");
                    bluetoothMac = btInfo.getString("macAddress");
                }
            } catch (JSONException e) {
                Log.e(TAG, "parseBluetoothInfo: ", e);
            }
        }
    }

    private void parseDeviceSignatureMapInfo(JSONObject userDeviceInfo, File dir) {
        // TODO:
    }

    private void parseDrmInfo(JSONObject userDeviceInfo, File dir) {
        try {
            JSONObject obj = userDeviceInfo.optJSONObject("drm");
            mDrmInfo = obj == null ? "" : obj.toString();
        } catch (Exception e) {
            Log.d(TAG, "parseDrmInfo exception=" + e);
        }
    }

    private void parseInputMethodInfo(JSONObject userDeviceInfo, File dir) {
        JSONObject inputObj = userDeviceInfo.optJSONObject("inputMethod");
        if (inputObj == null) {
            Log.i(TAG, "parseInputMethodInfo: no inputMethod data");
            return;
        }
        mInputMethodInfo = inputObj.toString();
    }

    private void parseInstalledAccessibilityServiceListInfo(JSONObject userDeviceInfo, File dir) {
        MockAccessibilityImpl.getInstance().parseInstalledAccessibilityServiceListInfo(mContext, userDeviceInfo);
        //return new ParserResult(status, "installed accessibility service info parse " + message);
    }

    private void parseSimCardsInfo(JSONObject userDeviceInfo, File dir) {
        try {
            JSONArray array = userDeviceInfo.optJSONArray("simCards");
            mSimInfo = array.toString();
        } catch (Exception e) {
            mSimInfo = "";
        }
    }

    private void parseSystemSharedLibraryNamesListInfo(JSONObject userDeviceInfo, File dir) {
        // TODO:
    }

    private void parseTimezoneInfo(JSONObject userDeviceInfo, File dir) {
        // TODO:
    }

    private void parseUserAgent(JSONObject userDeviceInfo, File dir) {
        try {
            String userAgent = userDeviceInfo.optString("userAgent");
            if (TextUtils.isEmpty(userAgent)) {
                Slog.w(TAG, "userDeviceInfo userAgent is empty!");
            } else {
                mUserAgent = userAgent;
            }
        } catch (Exception e) {
            Log.d(TAG, "parseWifiMacInfo exception=" + e);
        }
    }

    private void parseWifiMacInfo(JSONObject userDeviceInfo, File dir) {
        try {
            String wifiMac = userDeviceInfo.optString("wifiMac");
            if (TextUtils.isEmpty(wifiMac)) {
                Slog.w(TAG, "userDeviceInfo wifiMac is empty!");
            } else {
                SystemProperties.set("ro.cust.wifi.mac", wifiMac);
            }
        } catch (Exception e) {
            Log.d(TAG, "parseWifiMacInfo exception=" + e);
        }
    }

    private File prepareWorkspaceDir(int userId) {
        File file = new File(new File(workspaceDir), userId + "");
        if (!file.exists()) {
            boolean mkdirs = file.mkdirs();
            if (!mkdirs) {
                throw new IllegalStateException("Can't create work dir");
            }
        }

        userWorkspaceDir = file.getAbsolutePath();
        return file;
    }

    public void loadEnv(int userId) {
        boolean isPs = isPsUser(userId);
        if (isPs) {
            loadCustProp(userId);
            ParserResult result = loadStaticEnv(userId);
            if (result.status != Status.SUCCESS) {
                Slog.e(TAG, "load static env error -> " + result);
                resetEnv(userId);
                return;
            }
            loadDynamicEnv(userId);
        } else {
            resetEnv(userId);
        }
    }

    public void loadDynamicEnv(int userId) {
        // TODO:
    }

    public void resetEnv(int userId) {
        if (!rebootRequired) {
            //　重置属性
            SystemProperties.set("ro.cust.ps", "0");
        }

        bluetoothName = "";
        bluetoothMac =  "";
        networkInterfaceList = "";
        MockAccessibilityImpl.getInstance().cleanMockAccessibilityInfo();
        MockMemoryImpl.getInstance().resetMockMemoryInfo();
        MockStatFsImpl.getInstance().resetMockStructStatVfs();
        mSensorInfo = "";
        mDisplays = "";
        mInputMethodInfo = "";
        mInputDeviceInfo = "";
    }

    public void cleanupEnvFiles(int userId) {
        // TODO:
    }
    private void resetProps() {
        if (resultMapPsUsed.size() > 0) {
            resultMapPsUsed.forEach((key, value) -> {
                    SystemProperties.set(key.toString(), "");
            });
        }
        resultMapPsUsed.clear();
        Slog.d(TAG, "resetProps resultMapPsUsed.size = " + resultMapPsUsed.size());
    }
    public static Map<String, Object> parseData(String data) {
        Map<String, Object> resultMap = new HashMap<>();

        String[] pairs = data.split("\n");
        Log.d(TAG, "parseData pairs : " + pairs.toString());
        for (String pair : pairs) {
            String[] keyValue = pair.split("]:");
            String key = keyValue[0].trim().replaceAll("\\[|\\]", "");
            String valueString = keyValue[1].trim().replaceAll("\\[|\\]", "");
            resultMap.put(key, valueString);
        }

        return resultMap;
    }

    // 判断用户[userId]是否需要改机, 只用在改机生效前判断当前是否要改机，其它情况请直接读ro.cust.ps的属性值
    private boolean isPsUser(int userId) {
        File dir = prepareWorkspaceDir(userId);
        File file = new File(dir, mCustPropFileName);
        if (!file.exists()) {
            Log.e(TAG, "isPsUser cust file not exists userId=" + userId);
            return false;
        }

        Properties props = loadProperties(file);
        String ps = props.getProperty("ro.cust.ps", "0");
        return !ps.equals("0");
    }

    private Properties loadProperties(File file) {
        Properties props = new Properties();
        try {
            props.load(Files.newInputStream(file.toPath()));
        } catch (Exception e) {
            Log.e(TAG, "loadProperties error " + e);
        }
        return props;
    }

    private void initCustPropFile(File dir) {
        File custProp = new File(dir, mCustPropFileName);
        FileWriterWrapper fileWriter = new FileWriterWrapper(custProp, true);
        fileWriter.write("ro.cust.ps=1\n");
        fileWriter.close();
    }

    private void loadCustProp(int userId) {
        File dir = prepareWorkspaceDir(userId);
        File file = new File(dir, mCustPropFileName);
        Properties props = loadProperties(file);
        props.forEach((key, val) -> SystemProperties.set(key.toString(), val.toString()));
    }

    public enum Status {
        SUCCESS,
        FAILED,
        FAILED_USERID,
    }

    static class ParserResult {
        public final Status status;
        public final String reason;

        public ParserResult(Status status, @Nullable String reason) {
            this.status = status;
            this.reason = reason;
        }

        @Override
        public String toString() {
            return "status = " + status + ", reason = " + reason;
        }
    }
    private void parseBaseStationInfo(JSONObject object, File dir) {
        try {
            JSONObject location = object.optJSONObject("location");
            JSONObject baseStation = location.optJSONObject("baseStation");
            JSONArray array = baseStation.optJSONArray("neighboringCellInfo");
            mBaseStationInfo = baseStation.toString();
            mNeighboringCellInfo = array.toString();
        } catch (Exception e) {
            e.printStackTrace();
            mBaseStationInfo = "";
            mNeighboringCellInfo = "";
        }
    }
    private void parseWifisInfo(JSONObject object, File dir) {
        try {
            JSONObject location = object.optJSONObject("location");
            JSONArray array = location.optJSONArray("wifis");
            mWifisInfo = array.toString();
        } catch (Exception e) {
            e.printStackTrace();
            mWifisInfo = "";
        }
    }

    // add by yhy (start)
    private void parseSettingsInfo(JSONObject deviceInfo, File dir) {
        if (!deviceInfo.has("settings")) {
            mSettingsInfo = null;
            return;
        }
        try {
            mSettingsInfo = deviceInfo.getJSONObject("settings").toString();
        } catch (JSONException e) {
            Log.e(TAG, "parseSettingsInfo: e:" + e);
        }
    }
    // add by yhy (end)
    private native int nativeSaveFile(String fileName, String fileContent);
}
